package com.gorkemozbek.rubic.util

import java.lang.ref.{ReferenceQueue, WeakReference}
import java.util.WeakHashMap
import java.util.concurrent.locks.ReentrantReadWriteLock

abstract class InternCache[K, V >: Null] {

  private val queue = new ReferenceQueue[V]
  private val rwl = new ReentrantReadWriteLock()
  private val rlock = rwl.readLock
  private val wlock = rwl.writeLock
  private val map = new WeakHashMap[K, WeakReference[V]]

  protected def valueFromKey(k: K): V
  protected def keyFromValue(v: V): Option[K]

  def apply(name: K): V = {
    def cached(): V = {
      rlock.lock
      try {
        val reference = map get name
        if (reference == null) null
        else reference.get
      }
      finally rlock.unlock
    }
    def updateCache(): V = {
      wlock.lock
      try {
        val res = cached()
        if (res != null) res
        else {
          val sym = valueFromKey(name)
          map.put(name, new WeakReference(sym, queue))
          sym
        }
      }
      finally wlock.unlock
    }
    val res = cached()
    if (res == null) updateCache() else res
  }

  def unapply(other: V): Option[K] = keyFromValue(other)

}